home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / nn.zip / MASTER.C < prev    next >
C/C++ Source or Header  |  1989-12-31  |  16KB  |  773 lines

  1. /*
  2.  *    nn master daemon
  3.  *
  4.  *    maintains the article header database.
  5.  */
  6.  
  7. #include <signal.h>
  8. #include <errno.h>
  9. #include "config.h"
  10. #include "db.h"
  11.  
  12. /*
  13.  * nnmaster options:
  14.  *
  15.  *    -e N    expire a group if more than N articles are gone
  16.  *    -r N    repeat every N minutes
  17.  
  18.  *    -f    foreground execution (use with -r)
  19.  *    -y N    retry N times on error
  20.  *
  21.  *    -E    expire by recolleting entire groups rather than copying files
  22.  *    -C    check consistency of database on start-up
  23.  *    -b    include 'bad' articles (disables -B)
  24.  *    -B    remove 'bad' articles (just unlink the files)
  25.  *
  26.  *    -I    initialize
  27.  *    -t    trace collection of each group
  28.  *    -v    print version and exit
  29.  *    -u    update even if active is not modified
  30.  *    -w    send wakeup to real master
  31.  *    -Ltypes    exclude 'types' entries from the log
  32.  *    -D    debug
  33.  */
  34.  
  35. #include "options.h"
  36.  
  37. static int
  38.     initialize = 0,
  39.     prt_vers = 0,
  40.     unconditional = 0,
  41.     wakeup_master = 0,
  42.     expire_level = 1,
  43.     clean_to_expire = 0,
  44.     check_on_startup = 0,
  45.     repeat_delay = 0,
  46.     foreground = 0,
  47.     debug_mode = 0;
  48.  
  49. import int
  50.     ignore_bad_articles,
  51.     remove_bad_articles,
  52.     retry_on_error;
  53.  
  54. import char
  55.     *log_entry_filter;
  56.  
  57. export int
  58.     trace = 0,
  59. #ifdef NNTP
  60.     silent = 1,
  61. #endif
  62.     Debug = 0;
  63.  
  64.  
  65. Option_Description(master_options) {
  66.  
  67.     'I', Bool_Option( initialize ),
  68.     'v', Bool_Option( prt_vers ),
  69.     'u', Bool_Option( unconditional ),
  70.     'w', Bool_Option( wakeup_master ),
  71.  
  72.     'e', Int_Option( expire_level ),
  73.     'r', Int_Option_Optional( repeat_delay, 10 ),
  74.  
  75.     'f', Bool_Option( foreground ),
  76.     'y', Int_Option( retry_on_error ),
  77.  
  78.     'E', Bool_Option( clean_to_expire ),
  79.     'C', Bool_Option( check_on_startup ),
  80.     'b', Bool_Option( ignore_bad_articles ),
  81.     'B', Bool_Option( remove_bad_articles ),
  82.  
  83.     'L', String_Option( log_entry_filter ),
  84.     'D', Bool_Option( debug_mode ),
  85.     't', Bool_Option( trace ),
  86.  
  87.     '\0',
  88. };
  89.  
  90.  
  91. import FILE *master_file;
  92. import char news_active[];
  93.  
  94. static int rm_mpid_on_exit = 0;
  95.  
  96. extern long collect_group();
  97. extern long expire_group();
  98.  
  99. main(argc, argv)
  100. int argc;
  101. char **argv;
  102. {
  103.     time_t             age_active;
  104.     register group_header    *gh;
  105.     register int        cur_group;
  106.     int                col_article_count, col_group_count;
  107.     int                exp_article_count, exp_group_count;
  108.     long            temp;
  109.     time_t            start_time;
  110.     FILE            *m_pid;
  111.     
  112.  
  113.     umask(002);            /* avoid paranoia */
  114.     
  115.     init_global(1);
  116.  
  117.     parse_options(argc, argv, (char *)NULL, master_options, (char *)NULL);
  118.  
  119.     if (wakeup_master) {
  120.     if (!kill_master(SIGALRM) && errno == ESRCH)
  121.         printf("master is not running\n");
  122.     nn_exit(0);
  123.     }
  124.     
  125.     if (prt_vers) {
  126.     print_version("Master: Release %R.%V.%P, Compilation %U\n");
  127.     nn_exit(0);
  128.     }
  129.  
  130.     if (kill_master(SIGALRM)) {
  131.     printf("The master is already running\n");
  132.     nn_exit(0);
  133.     }
  134. #ifdef NNTP
  135.     nntp_check();
  136. #endif
  137.     if (initialize) {
  138.     build_master();
  139.     nn_exit(0);
  140.     }
  141.  
  142.     if (!debug_mode) {
  143.     close(0);
  144.     close(1);
  145.     close(2);
  146.     if (open("/dev/null", 2) == 0) dup(0), dup(0);
  147.     }
  148.     
  149.     if (repeat_delay && !debug_mode && !foreground) {
  150.     while ((temp = fork()) < 0) sleep(1);
  151.     if (temp) nn_exit(0);
  152.  
  153.     process_id = getpid();    /* init_global saved parent's pid */
  154.     
  155.     DETATCH_TERMINAL
  156.     }
  157.  
  158.     rm_mpid_on_exit = 1;
  159.     m_pid = open_file(relative(lib_directory, "MPID"),
  160.               OPEN_CREATE|MUST_EXIST);
  161.     fprintf(m_pid, "%d\n", process_id);
  162.     fclose(m_pid);
  163.  
  164.     log_entry('M', "Master started -r%d -e%d%s", 
  165.                repeat_delay, expire_level, clean_to_expire ? " -E" : "");
  166.  
  167.     if (check_on_startup) {
  168.     char cmd[FILENAME];
  169.     sprintf(cmd, "%s/nnadmin Z", BIN_DIRECTORY);
  170.     system(cmd);
  171.     log_entry('M', "Database validation completed");
  172.     }
  173.     
  174.     repeat_delay *= 60;
  175.     
  176.     init_digest_parsing();
  177.     
  178.     open_master(OPEN_READ);
  179.     close_master();
  180.  
  181.     open_master(OPEN_UPDATE);
  182.     
  183.  again:
  184.  
  185. #ifdef NNTP
  186.     if (use_nntp) {
  187.     if (nntp_get_active() < 0) {
  188.         nntp_close_server();
  189.         current_group = NULL; /* for init_group */
  190.         log_entry('N', "Can't access active file --- %s",
  191.               repeat_delay ? "sleeping" : "terminating");
  192.         if (repeat_delay == 0)
  193.         nn_exit(1);
  194.         sleep(repeat_delay);
  195.         goto again;
  196.     }
  197.     }
  198. #endif
  199.  
  200.     age_active = file_exist(news_active, "fr");
  201. #ifdef NNTP
  202.     if (!use_nntp)
  203. #endif
  204.     if (age_active == (time_t)0)
  205.         sys_error("Cannot access active file");
  206.  
  207.     temp = receive_admin();
  208.     
  209.     if (!temp && !unconditional && age_active <= master.last_scan) {
  210.     if (repeat_delay == 0)
  211.         goto out;
  212.     
  213.     if (s_hangup) goto out;
  214. #ifdef NNTP
  215.     if (use_nntp)
  216.         nntp_cleanup();
  217. #endif /* NNTP */
  218.     if (debug_mode) 
  219.         printf("NONE (*** SLEEP ***)\n");
  220.     else {
  221.         if (trace) log_entry('T', "none");
  222.         sleep(repeat_delay);
  223.     }
  224.     
  225.     if (s_hangup) goto out;
  226.  
  227.     goto again;
  228.     }
  229.  
  230.     unconditional = 0;    /* only first pass */
  231.     
  232.     time(&start_time);
  233.     col_article_count = col_group_count = 0;
  234.     exp_article_count = exp_group_count = 0;
  235.     
  236.     visit_active_file();
  237.         
  238.     for (cur_group = 0; cur_group < master.number_of_groups; cur_group++) {
  239.  
  240.     if (s_hangup) break;
  241.  
  242.     gh = &active_groups[cur_group];
  243.  
  244.     if (gh->last_l_article  > gh->last_article || 
  245.         gh->first_l_article > gh->first_article) {
  246.         log_entry('X', "group %s renumbered", gh->group_name);
  247.         clean_group(gh);
  248.  
  249.         goto do_collect;
  250.     }
  251.  
  252.     if (expire_level > 0 && gh->last_l_article > 0) {
  253.         if ((gh->first_l_article + expire_level) <= gh->first_article) {
  254.         if (trace) log_entry('T', "%s expire level", gh->group_name);
  255.         gh->group_flag |= G_EXPIRE;
  256.         goto do_collect;
  257.         }
  258.         
  259.         if (gh->first_article > gh->last_l_article) {
  260.         if (trace) log_entry('T', "%s expire void", gh->group_name);
  261.         clean_group(gh);
  262.         goto do_collect;
  263.         }
  264.     }
  265.  
  266.     if (gh->last_l_article == gh->last_article) {
  267.         if (gh->group_flag & G_BLOCKED) goto unblock_group;
  268.         continue;
  269.     }
  270.     
  271.      do_collect:
  272.     if (!init_group(gh)) {
  273.         if ((gh->group_flag & G_NO_DIRECTORY) == 0) {
  274.         log_entry('R', "%s: no directory", gh->group_name);
  275.         gh->group_flag |= G_NO_DIRECTORY;
  276.         }
  277.         gh->last_l_article = gh->last_article;
  278.         gh->first_l_article = gh->last_article;    /* OBS: not first */
  279.         gh->group_flag &= ~(G_EXPIRE | G_BLOCKED);        
  280.         save_group(current_group);
  281.         continue;
  282.     }
  283.     
  284.     if (gh->group_flag & G_NO_DIRECTORY) {
  285.         /* The directory has been created now */
  286.         gh->group_flag &= ~G_NO_DIRECTORY;
  287.         clean_group(gh);
  288.     }
  289.     
  290.     if (gh->group_flag & G_EXPIRE) {
  291.         if (clean_to_expire) {
  292.         temp = gh->first_article - gh->first_l_article;
  293.         clean_group(gh);
  294.         } else {
  295.         if ((gh->group_flag & G_BLOCKED) == 0) {
  296.             gh->group_flag |= G_BLOCKED;
  297.             save_group(gh);
  298.         }
  299.         temp = expire_group(gh);
  300.         }
  301.         if (temp) {
  302.         exp_article_count += temp;
  303.         exp_group_count++;
  304.         }
  305.     }
  306.         
  307.     temp = collect_group(gh);
  308. #ifdef NNTP
  309.     if (temp < 0) {
  310.         /* connection broken */
  311.         gh->group_flag &= ~G_EXPIRE;    /* remains blocked */
  312.         save_group(gh);
  313.         age_active = master.last_scan;     /* did not complete */
  314.         current_group = NULL; /* for init_group */
  315.         break;
  316.     }
  317. #endif
  318.     if (temp > 0) {
  319.         col_article_count += temp;
  320.         col_group_count++;
  321.     }
  322.  
  323.      unblock_group:
  324.     if (temp || (gh->group_flag & G_BLOCKED)) {
  325.         gh->group_flag &= ~(G_EXPIRE | G_BLOCKED);
  326.         save_group(gh);
  327.     }
  328.     }
  329.  
  330.     /* if master is interrupted, all new articles may not be collected */
  331.  
  332.     if (!s_hangup) 
  333.     master.last_scan = age_active;
  334.  
  335.     save_master();
  336.  
  337.     if (exp_article_count)
  338.     log_entry('X', "Expire: %3d art, %2d gr, %2ld s %s",
  339.           exp_article_count, exp_group_count, 
  340.           time((time_t *)0) - start_time,
  341.           col_article_count ? "(incl. collect)" : "");
  342.  
  343.     if (col_article_count)
  344.     log_entry('C', "Collect: %3d art, %2d gr, %2ld s %s",
  345.           col_article_count, col_group_count,
  346.           time((time_t *)0) - start_time,
  347.           exp_article_count ? "(incl. expire)" : "");
  348.     
  349.     if (!s_hangup && repeat_delay) {
  350. #ifdef NNTP
  351.     if (use_nntp)
  352.         nntp_cleanup();
  353. #endif /* NNTP */
  354.     if (!debug_mode) sleep(repeat_delay);
  355.     if (!s_hangup) goto again;
  356.     }
  357.     
  358.  out:
  359.     nn_exit(0);
  360.     /*NOTREACHED*/
  361. }
  362.  
  363.  
  364. /* 
  365.  * nn_exit() --- called whenever a program exits.
  366.  */
  367.  
  368. nn_exit(n)
  369. {
  370. #ifdef NNTP
  371.     if (use_nntp)
  372.     nntp_cleanup();
  373. #endif /* NNTP */
  374.     close_master();
  375.  
  376.     if (rm_mpid_on_exit)
  377.     unlink(relative(lib_directory, "MPID"));
  378.  
  379.     if (n)
  380.     log_entry('E', "Abnormal termination, exit=%d", n);
  381.     else
  382.     if (rm_mpid_on_exit)
  383.     log_entry('M', "Master terminated%s", s_hangup ? " (hangup)" : "");
  384.  
  385.     exit(n);
  386. }
  387.  
  388.  
  389. /*
  390.  * add new group to master file
  391.  */
  392.  
  393. group_header *add_new_group(name)
  394. char *name;
  395. {
  396.     register group_header *gh;
  397.     FILE *group_file;
  398.     static has_warned = 0;
  399.     
  400.     if (master.free_groups <= 0) {
  401.     if (!has_warned) {
  402.         log_entry('R', "NO MORE FREE GROUP SLOTS -- RESTART MASTER");
  403.         has_warned++;
  404.     }
  405.     return NULL;
  406.     }
  407.     
  408.     master.free_groups--;
  409.     
  410.     gh = &active_groups[master.number_of_groups];
  411.     sorted_groups[master.number_of_groups] = gh;
  412.     
  413.     gh->group_name_length = strlen(name);
  414.     gh->group_name = (char *)malloc(gh->group_name_length + 1);
  415.     mem_check(gh->group_name, gh->group_name_length, "bytes for group name");
  416.     strcpy(gh->group_name, name);
  417.     
  418.     gh->group_num = master.number_of_groups++;
  419.     
  420.     group_file = open_groups(OPEN_UPDATE|MUST_EXIST);
  421.     
  422.     fseek(group_file, master.next_group_write_offset, 0);
  423.     name[gh->group_name_length] = NL;
  424.     Fwrite(name, sizeof(char), gh->group_name_length + 1, group_file);
  425.     name[gh->group_name_length] = NUL;
  426.     fclose(group_file);
  427.     
  428.     master.next_group_write_offset += gh->group_name_length + 1;
  429.  
  430.     clean_group(gh);
  431.     
  432.     save_master();
  433.  
  434.     sort_groups();
  435.  
  436.     log_entry('C', "new group: %s (%d)", gh->group_name, gh->group_num);
  437.     
  438.     return gh;
  439. }
  440.  
  441.  
  442. save_group(gh)
  443. group_header *gh;
  444. {
  445.     int32 flag;
  446.     
  447.     flag = gh->group_flag;
  448.     gh->group_flag &= G_MASTER_FLAGS;
  449.  
  450.     if (!db_write_group(master_file, gh, gh->group_num))
  451.     write_error();
  452.     fflush(master_file);
  453.     
  454.     gh->group_flag = flag;
  455. }
  456.  
  457.  
  458. save_master()
  459. {
  460.     rewind(master_file);
  461.     if (!db_write_master(master_file, &master))
  462.     write_error();
  463.     fflush(master_file);
  464. }
  465.  
  466.  
  467. clean_group(gh)
  468. register group_header *gh;
  469. {
  470.     if (trace)
  471.     log_entry('T', "CLEAN %s", gh->group_name);
  472.     
  473.     gh->first_l_article = 0;
  474.     gh->last_l_article = 0;
  475.  
  476.     gh->index_write_offset = (off_t)0;
  477.     gh->data_write_offset = (off_t)0;
  478.  
  479.     gh->group_flag &= ~G_EXPIRE;
  480.     gh->group_flag |= G_BLOCKED;
  481.     
  482.     save_group(gh);
  483. }
  484.  
  485. /*
  486.  *    Build initial master file ; calls Initialize script and
  487.  *    reads info from its standard output
  488.  */
  489.  
  490. build_master()
  491. {
  492.     char command[512];
  493.     char groupname[512];
  494.     group_header group;
  495.     FILE *group_file, *src;
  496.     int lcount, use_group_file;
  497.  
  498.  
  499.     printf("Confirm initialization by typing 'OK': ");
  500.     fl;
  501.     gets(command);
  502.     if (strcmp(command, "OK")) {
  503.     printf("No initialization\n");
  504.     nn_exit(0);
  505.     }
  506.  
  507.     printf("Initializing master data base...");
  508.     fl;
  509.     
  510.     if (chdir(lib_directory) < 0)    /* so we can use open_file */
  511.     sys_error("lib");
  512.  
  513. #ifdef NNTP
  514.     if (use_nntp && nntp_get_active() < 0)
  515.         sys_error("Can't get active file");
  516. #endif
  517.     /* check active file for duplicates */
  518.  
  519.     sprintf(command, "awk 'NF>0{print $1}' %s | sort | uniq -d", news_active);
  520.     
  521.     src = popen(command, "r");
  522.  
  523.     for (lcount = 0; fgets(groupname, 512, src); lcount++) {
  524.     if (lcount == 0)
  525.         printf("\n%s contains duplicate entries for the following groups:",
  526.            news_active);
  527.         
  528.     fputs(groupname, stdout);
  529.     }
  530.  
  531.     pclose(src);
  532.  
  533.     if (lcount > 0) {
  534.     printf("Do you want to repair this file before continuing ? (y)");
  535.     gets(command);
  536.     if (command[0] == NUL || command[0] == 'y' || command[0] == 'Y')
  537.         nn_exit(0);
  538.     }
  539.  
  540.     /* if a "GROUPS" file exist offer to use that, else */
  541.     /* read group names from active file */
  542.  
  543.     use_group_file = 0;
  544.     
  545.     if (src = open_groups(OPEN_READ)) {
  546.     printf("\nA GROUPS file already exist -- reuse it? (y)");
  547.     fl;
  548.     gets(command);
  549.     if (command[0] == NUL || command[0] == 'y' || command[0] == 'Y') {
  550.         use_group_file = 1;
  551.     } else
  552.         fclose(src);
  553.     }
  554.     
  555.     if (!use_group_file) {
  556.     strcpy(command, "awk 'NF>0{print $1}' ");
  557.     strcat(command, news_active);
  558.     strcat(command, " | sort -u");
  559.         
  560.     src = popen(command, "r");
  561.  
  562.     group_file = open_groups(OPEN_CREATE|MUST_EXIST);
  563.     }
  564.     
  565.     open_master(OPEN_CREATE);
  566.  
  567.     fseek(master_file, (off_t)sizeof(master), 0);
  568.     
  569.     master.number_of_groups = 0;
  570.     
  571.     while (fgets(groupname, 512, src)) {
  572.  
  573.     group.group_num = master.number_of_groups++;
  574.     
  575.     group.group_name_length = strlen(groupname) - 1;    /* strip NL */
  576.     groupname[group.group_name_length] = NUL;
  577.     group.group_name = groupname;
  578.  
  579.     group.group_flag = 0;
  580.  
  581.     init_group(&group);
  582.  
  583.     clean_group(&group);
  584.     
  585.     /* moderation flag will be set by first visit_active_file call */
  586.  
  587.     if (strcmp(groupname, "control") == 0)
  588.         group.group_flag |= G_CONTROL;
  589.     
  590.     save_group(&group);
  591.  
  592.     if (!use_group_file) {
  593.         groupname[group.group_name_length] = NL;
  594.         Fwrite(groupname, sizeof(char), 
  595.            group.group_name_length + 1, group_file);
  596.     }
  597.     }
  598.  
  599.     if (use_group_file) {
  600.     master.next_group_write_offset = ftell(src);
  601.     fclose(src);
  602.     } else {
  603.     master.next_group_write_offset = ftell(group_file);
  604.     fclose(group_file);
  605.     pclose(src);
  606.     }
  607.     
  608.     master.last_scan = 0;
  609.  
  610.     save_master();
  611.     
  612.     close_master();
  613.  
  614.     printf("done\n");
  615.  
  616.     log_entry('M', "Master data base initialized");
  617.     
  618.     fl;
  619. }
  620.  
  621.  
  622. /*
  623.  * receive commands from administrator
  624.  */
  625.  
  626. receive_admin()
  627. {
  628.     FILE *gate;
  629.     char buffer[128], *bp;
  630.     char command, *user_date;
  631.     long arg1, arg2;
  632.     int must_collect;
  633.     register group_header *gh;
  634.     
  635.     gate = open_file(relative(lib_directory, "GATE"), OPEN_READ | OPEN_UNLINK);
  636.     if (gate == NULL) return 0;
  637.     
  638.     sleep(2);    /* give administrator time to flush buffers */
  639.  
  640.     must_collect = 0;
  641.     
  642.     while (fgets(buffer, 128, gate)) {
  643.     bp = buffer;
  644.     command = *bp;
  645.     if ((bp = strchr(bp, ';')) == NULL) continue;
  646.     arg1 = atol(++bp);
  647.     if ((bp = strchr(bp, ';')) == NULL) continue;
  648.     arg2 = atol(++bp);
  649.     if ((bp = strchr(bp, ';')) == NULL) continue;
  650.     user_date = ++bp;
  651.     if ((bp = strchr(bp, ';')) == NULL) continue;
  652.     *bp++ = NUL;
  653.     if (*bp != NL) continue;
  654.     
  655.     log_entry('A', "RECV %c %ld %ld (%s)",
  656.           command, arg1, arg2, user_date);
  657.  
  658.     if (arg1 >= 0 && arg1 < master.number_of_groups)
  659.         gh = &active_groups[arg1];
  660.     else
  661.         gh = NULL;
  662.     
  663.     switch (command) {
  664.  
  665.      case 'r':
  666.         repeat_delay = arg1;
  667.         continue;
  668.         
  669.      case 'e':
  670.         expire_level = arg1;
  671.         continue;
  672.         
  673.      case 'X':    /* expire */
  674.         if (gh) {
  675.         gh->group_flag |= G_EXPIRE | G_BLOCKED;
  676.         save_group(gh);
  677.         break;
  678.         } 
  679.         visit_active_file();    /* just in case */
  680.         Loop_Groups_Header(gh) {
  681.         if (gh->first_l_article + arg2 < gh->first_article) {
  682.             gh->group_flag |= G_EXPIRE | G_BLOCKED;
  683.             save_group(gh);    /* could block here */
  684.         }
  685.         }
  686.         break;
  687.  
  688.      case 'S':    /* set flag */
  689.         gh->group_flag |= arg2;
  690.         save_group(gh);
  691.         continue;
  692.  
  693.      case 'C':    /* clear flag */
  694.         gh->group_flag &= ~arg2;
  695.         save_group(gh);
  696.         continue;
  697.  
  698.      case 'R':    /* recollect */
  699.         if (gh) {
  700.         clean_group(gh);
  701.         } else
  702.         Loop_Groups_Header(gh)
  703.             clean_group(gh);
  704.         break;
  705.  
  706.      case 'U':    /* unconditional pass */
  707.         unconditional++;
  708.         break;
  709.  
  710.      case 'T':    /* toggle trace flag */
  711.         trace = !trace;
  712.         continue;
  713.         
  714.      default:
  715.         continue;
  716.     }     
  717.     must_collect++;   
  718.     }
  719.     
  720.     fclose(gate);
  721.  
  722.     return must_collect;
  723. }    
  724.  
  725.  
  726. /*
  727.  * disk write with check -- halt if no space on disk
  728.  */
  729.  
  730.  
  731. Fwrite(buf, size, nitems, stream)
  732. char *buf;
  733. int size;
  734. int nitems;
  735. FILE *stream;
  736. {
  737.     if (fwrite(buf, size, nitems, stream) != nitems)
  738.     write_error();
  739. }
  740.  
  741. write_error()
  742. {
  743.     /*
  744.      * should wait for problems to clear out rather than die...
  745.      */
  746.     sys_error("DISK WRITE ERROR");
  747. }
  748.  
  749. /*
  750.  * dummy routines - should never be called by master
  751.  */
  752.  
  753. /*VARARGS*/
  754. user_error()
  755. {
  756.     dummy_error("user_error");
  757. }
  758.  
  759. dummy_error(name)
  760. char *name;
  761. {
  762.     sys_error("Dummy routine called by master: %s", name);
  763. }
  764.  
  765. #ifdef HAVE_JOBCONTROL
  766. suspend_nn()
  767. {}
  768. #endif
  769.  
  770. #ifdef NNTP /* XXX */
  771. msg() {}
  772. #endif /* NNTP Bogus */
  773.